/*
 *Basado en el libro de Duane A. Bailey, "Java Structures", pág. 305
 */
package Hoja6;


/**
 *
 * @author María Fernanda Martínez
 */
public class HuffmanTree implements Comparable {
    BinaryTree root; // raíz del árbol
    int totalWeight; // frecuencia acumulada del arból
    int count=-1;    // contador de caracteres

    /**
     * Nombre: HuffmanTree
     * Descripción: constructor de la clase
     * Pre: e no es null
     * Post: construye una Leaf con el caracter asociado.
     * No hay valor de retorno
     * @param e
     */
    public HuffmanTree(Leaf e)
    {
        root = new BinaryTree(e);
        totalWeight = e.frequency;
    }

    /**
     * Nombre: HuffmanTree
     * Descripción: constructor de la clase con árboles Huffman asociados
     * Pre: left y right no son null
     * Post: union de dos árboles y sus frecuencias acumuladas
     * No hay valor de retorno
     * @param left
     * @param right
     */
    public HuffmanTree(HuffmanTree left, HuffmanTree right)
    {
        this.totalWeight = left.totalWeight + right.totalWeight;
        root = new BinaryTree(null,left.root,right.root);
    }

    /**
     * Nombre: compareTo
     * Descripción: comparar el valor de la variable con el parámetro
     * Pre: other no es null
     * Post: regresar entero reflejando la relación de los valores.
     */
    public int compareTo(Object other)
    {
        HuffmanTree that = (HuffmanTree)other;
        return this.totalWeight - that.totalWeight;
    }

    /**
     * Nombre: equals
     * Descripción: verificar si el valor de dos variables son iguales
     * Pre: no hay
     * Post: regresa true si son iguales
     * No hay valor de retorno
     * @param that
     */
    public boolean equals(Object that)
    // post: return true if this and that are same tree instance
    {
        return this == that;
    }
    
    /**
     * Nombre: print
     * Descripción: llamar a print con parámetros
     * Pre: no hay
     * Post: imprime cadenas asociadas al carácter en el árbol
     * No hay valor de retorno ni parámetros
     */
    public void print()
    {
        print(this.root,"");
    }

    /**
     * Nombre: print
     * Descripción: imprime la representación de los caracteres en el árbol
     * Pre: no hay
     * Post: imprime las cadenas asociadas a los caracteres de r, prefijados representation
     * @param r
     * @param representation
     */
    protected void print(BinaryTree r, String representation)
    {
        if (!r.left().isEmpty())
        {   // nodo interior
            print(r.left(),representation+"0"); // Agregar 0
            print(r.right(),representation+"1"); // Agregar 1
        } else { // Leaf; imprimir codificación
            Leaf e = (Leaf)r.value();
            System.out.println("Encoding of "+e.ch+" is "+representation+" (frequency was "+e.frequency+")");            
        }   
    }
    
    /**
     * Nombre: decode
     * Descripción: llama a decode con parámetros
     * Pre: no hay
     * Post: imprime caracter asociado a combinación de números
     * No hay valor de retorno
     * @param mensaje
     */
    public void decode(String mensaje){
        decode(this.root,mensaje);
    }
    
    /**
     * Nombre: decode
     * Descripción: imprimir caracteres asociados a una combinación de 0 y 1
     * Pre: no hay
     * Post: mensaje decodificado impreso
     * No hay valor de retorno
     * @param r
     * @param message
     */
    protected void decode(BinaryTree r, String message){
        count++; 
        if(count<=message.length()){
            if(!r.left().isEmpty()){
                if(count!=message.length()){
                    if(message.charAt(count)=='0'){
                        decode(r.left(),message);
                    }
                    else if(message.charAt(count)=='1'){
                        decode(r.right(),message);
                    }
                }
            } else{
                Leaf e = (Leaf)r.value();
                System.out.print(e.ch);
                count--;
                if(count!=message.length()&&count>0){
                    if(message.charAt(count)=='0'||message.charAt(count)=='1'){
                        decode(message);
                    }
                }
            }
        }     
    }    
}
